'''
Created on Aug 31, 2011

@author: bstar
'''
import os
import traceback
from txrdq.rdq import ResizableDispatchQueue
from twisted.internet import reactor, defer
from twisted.protocols.ftp import FTPClient
from twisted.internet.protocol import ClientCreator
from dopplershift.tx.clients.ftp.producer import FileChunkProducer
from twisted.protocols.ftp import ConnectionLost
import logging
from dopplershift.core.patterns.mapping import DSMapObject
logger = logging.getLogger(__name__)

FTP_CODE_CONNECTION_LOST = 1
FTP_CODE_CODE_UNKNOWN_ERROR = 2

class DSFtpClient:
    _pass = ''
    _username = 'user'
    port = 21
    host = 'localhost'
    _defer = None
    _ftpFiles = None
    _connected = None
    _working = True
    reconnect = 10
    normal_priority = 10
    resend_priority = 15
    auto_reconnection = True
    def __init__(self, 
                 host='localhost',
                 port=21,
                 username='user',
                 password='password',
                 path='/',
                 upload_callback=None, 
                 queue_end_callback=None, auto_reconnection=True,
                 upload_errback=None
                 
                 ):
        
        self.host = host
        self.port = int(port)
        self._username = username
        self._pass = password
        self.path = path
        self._queue = ResizableDispatchQueue(self.work)
        
        self.auto_reconnection = auto_reconnection
        
        self._upload_callback = upload_callback
        self._queue_end_callback = queue_end_callback
        self._upload_errback = upload_errback
    def send(self, file, priority):
        self._queue.put(file, priority)
    def queue(self, filelist):
        for el in filelist:
            self.send(el, self.normal_priority)
    def connect(self):
        if self._connected != None:
            return
        self._connected = False
        self.creator = ClientCreator(reactor, FTPClient, self._username,self._pass, passive=True)
        defer = self.creator.connectTCP(self.host, self.port)
        defer.addCallback(self.connectionMade).addErrback(self.connectionFail)
    def connectionFail(self, reason):
        logger.debug("Connection fail.")

        if isinstance(self.reconnect, int):
            logger.debug("Start reconnect after %s seconds."%self.reconnect)
            reactor.callLater(self.reconnect, self.connect)
    def work(self, upload_file):
        logger.debug("Prepare to sending %s"%upload_file)
        deffereds = []
        
        if os.path.exists(upload_file):
            logger.debug("Create deferred for storing file %s"%upload_file)
            store, finish = self.client.storeFile("%s/%s"%(self.path, os.path.basename(upload_file)))
            finish.addCallback(self._upload_end, upload_file).addErrback(self.uploadError, upload_file)
            store.addCallback(self.send_file, upload_file).addErrback(self.storeError, upload_file)
            deffereds.append(store)
            deffereds.append(finish)
        defer.gatherResults(deffereds)
    def connectionMade(self,ftpClient):
        """Checks if there are more files to upload and uploads them.
        The main point with this method is that you have to collect the defereds of each command
        and wait until they succeed.
        """
        self._connected = True
        print ftpClient
        logger.debug("Connection made. Create deffereds for sotring files")
        "We'll need the client to do a disconnect later"
        self.client = ftpClient
        self.next()
    def cleaning(self, result, sender, upload_file):
        try:
            sender.unregisterProducer()
            sender.finish()
        finally:
            pass
    def send_file(self, sender, upload_file):
        fp = open(upload_file,'r')
        producer = FileChunkProducer(sender.transport, upload_file)
        d = producer.getDeferred()
        try:
            sender.registerProducer(producer, False)
        except:
            traceback.print_exc()
        d.addCallback(self.cleaning, sender, upload_file)
    def _upload_end(self, ftpResult, upload_file):
        ''''
        '''
        try:
            if ftpResult[0][1][0][1][1] == '226 Transfer complete.':
                if self._upload_callback is not None:
                    self._upload_callback(upload_file)
        except:
            traceback.print_exc()
    
        if sum(self._queue.size()) == 0 and self._queue_end_callback is not None:
            self._queue_end_callback()
        self.next()
    def stop(self):
        self.client.quit()
        self._queue.stop()
        self._working = False
    def next(self):
        if self._working == True:
            self._queue.next()
    def uploadError(self, err, uplfile):
        code = FTP_CODE_CODE_UNKNOWN_ERROR
        if isinstance(err.value, ConnectionLost):
            print "CONNECTION LOST", uplfile
            code=FTP_CODE_CONNECTION_LOST
            if self.auto_reconnection:
                reactor.callLater(10, self.connect)
        if self._upload_errback is not None:
            self._upload_errback(DSMapObject(file=uplfile,code=code))            
        logger.debug("Uploading %s file error"%uplfile)

    def storeError(self, err, uplfile):
        logger.debug("Storing %s file error"%uplfile)